/* parse.c */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "parse.h"

static int char_is_single_char_token(int c)
{
  return (c == ',' || c == ';' || c == '{' || c == '}' || c == '(' || c == ')' || c == '[' || c == ']' || c == '/' || c == '*' || c == '&');
}

static int read_next_token(FILE *f, char *token, int max_len)
{
  int c;
  int len = 0;

 START_READ:
  while ((c = getc(f)) != EOF && isspace(c))
    ;
  if (c == EOF)
    return 1;

  /* skip C comments */
  if (c == '/') {
    c = getc(f);
    if (c == '*') {
      while (42) {
        c = getc(f);
        while (c != EOF && c != '*')
          c = getc(f);
        if (c == EOF)
          return -1;
        c = getc(f);
        if (c == EOF)
          return -1;
        if (c == '/')
          break;
      }
      goto START_READ;
    } else if (c == '/') {
      while ((c = getc(f)) != EOF)
        if (c == '\n')
          break;
      if (c == EOF)
        return -1;
      goto START_READ;
    } else if (c != EOF)
      ungetc(c, f);
  }

  /* skip C preprocessor lines */
  if (c == '#') {
    while ((c = getc(f)) != EOF)
      if (c == '\n')
        break;
    if (c == EOF)
      return -1;
    goto START_READ;
  }

  /* return single-char tokens */
  if (char_is_single_char_token(c)) {
    token[0] = c;
    token[1] = '\0';
    return 0;
  }
  
  /* read multiple char tokens */
  token[len++] = c;
  while (len+1 < max_len && (c = getc(f)) != EOF) {
    if (isspace(c) || char_is_single_char_token(c)) {
      token[len] = '\0';
      ungetc(c, f);
      return 0;
    }

    token[len++] = c;
  }
  token[len] = '\0';

  return 0;
}

int parse_open(PARSE_STATE *state, const char *filename)
{
  state->f = fopen(filename, "r");
  if (state->f == NULL)
    return 1;

  strncpy(state->filename, filename, sizeof(state->filename));
  state->filename[sizeof(state->filename)-1] = '\0';

  state->token[0] = '\0';
  state->saved_token[0] = '\0';
  return 0;
}

char *parse_get_token(PARSE_STATE *state)
{
  if (state->saved_token[0] == '\0') {
    if (read_next_token(state->f, state->token, sizeof(state->token)) == 0)
      return state->token;
    return NULL;
  }

  strcpy(state->token, state->saved_token);
  state->saved_token[0] = '\0';
  return state->token;
}

void parse_unget_token(PARSE_STATE *state)
{
  strcpy(state->saved_token, state->token);
}

void parse_close(PARSE_STATE *state)
{
  fclose(state->f);
}
